package com.cyf.plc.plc

import com.cyf.library.util.loge
import com.cyf.library.util.logi
import com.cyf.plc.constant.Constants
import com.cyf.plc.model.PlcAttribute
import com.cyf.plc.model.PlcFault
import com.cyf.plc.plc.client.PlcClient
import com.cyf.library.thread.ThreadManager.startThread
import com.cyf.library.thread.ThreadManager.stopThreadExecutor
import com.cyf.plc.bean.*
import com.pixplicity.easyprefs.library.Prefs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.util.*
import kotlin.collections.HashMap

object PlcExecutor {

    private const val PLC_IP_TAG = "plc_ip"

        private const val PLC_IP_ADDRESS = "172.255.1.250"
//    private const val PLC_IP_ADDRESS = "192.168.124.10"

    fun getPlcIp(): String {
        return Prefs.getString(PLC_IP_TAG, PLC_IP_ADDRESS)
    }

    fun savePlcIp(plcIp: String) {
        Prefs.putString(PLC_IP_TAG, plcIp)
    }

    var roomSize = 1

    /**
     * 设置当前plc寄存器地址列表
     *
     * @return 地址信息列表
     */
    var plcAttributeList: List<PlcAttribute> = ArrayList()

    /**
     * 设置当前故障点列表
     */
    var plcFaultList: List<PlcFault> = ArrayList()

    /**
     * plc连接客户端，读写
     */
    private lateinit var plcClient: PlcClient

    /**
     * 初始化PLC客户端（使用线程池）
     *
     * @param ip               ip地址
     * @param startReadAddress 起始读取地址
     * @param bufferSize       buffer数组大小
     */
    fun initPlc(
        ip: String,
        startReadAddress: Int,
        bufferSize: Int
    ) {
        //初始化Plc客户端
        plcClient = PlcClient(ip, startReadAddress, bufferSize)
        //启动线程,一直在后台进程中进行读取
        startThread(plcClient, "Plc-client-$ip")
    }

    /**
     * 使用协程，启动监听plc通讯
     */
    fun initPlc2(
        ip: String,
        startReadAddress: Int,
        bufferSize: Int
    ) {

        //启动线程,一直在后台进程中进行读取
        GlobalScope.launch {
            withContext(Dispatchers.IO) {
                //初始化Plc客户端
                plcClient = PlcClient(ip, startReadAddress, bufferSize)
                plcClient.run()
            }
        }
    }

    /**
     * 关闭PLC客户端
     */
    fun closePlc() {
        plcClient.closeS7Client()
        stopThreadExecutor()
        "Close plc client.".loge()
    }

    /**
     *
     *写入值到plc，数值变化，等待变化之后异步处理
     */
    fun write(plcWriteAttribute: PlcWriteAttribute) {
        "Write:$plcWriteAttribute".logi()
        plcClient.putWriteBean(plcWriteAttribute)
    }

    /**
     * 读取plc地址
     *
     */
    fun read(readPlcAttribute: PlcAttribute) {
//        "Read:$readPlcAttribute".logi()
        synchronized(plcAttributeList) {
            for (plcAttribute in plcAttributeList) {
                if (plcAttribute.offSet == readPlcAttribute.offSet) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
    }

    fun read(offset: Int, unit: String) {
        synchronized(plcAttributeList) {
            for (plcAttribute in plcAttributeList) {
                if (plcAttribute.offSet == offset && plcAttribute.addressUnit.equals(unit)) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
    }

    var panelHashMap: HashMap<Int,PanelStatus> = HashMap()
    fun updatePlcSystem(){
        //VW
        var systemWind = plcClient.GetShortAt(Constants.SYSTEM_WIND_ID).toFloat()
        var systemMode = plcClient.GetShortAt(Constants.SYSTEM_MODE_ID).toFloat()
        var systemPower = plcClient.GetShortAt(Constants.SYSTEM_POWER_ID).toFloat()
        var systemLijia = plcClient.GetShortAt(Constants.SYSTEM_OUT_HOME_ID).toFloat()
        var systemJieNeng = plcClient.GetShortAt(Constants.SYSTEM_ENERGY_CONSERVATION_ID).toFloat()
        var warnFlag1 = plcClient.GetShortAt(Constants.SYSTEM_WARN1_ID).toFloat()
        var warnFlag2 = plcClient.GetShortAt(Constants.SYSTEM_WARN2_ID).toFloat()
        var noFault = (warnFlag1 == Constants.POWER_OFF && warnFlag2 == Constants.POWER_OFF)

        var sensorTemp = plcClient.GetShortAt(Constants.SENSOR_TEMP_ID).toFloat()/10f
        var sensorHumidity = plcClient.GetShortAt(Constants.SENSOR_HUMIDITY_ID).toFloat()/10f
        var sensorPM25 = plcClient.GetShortAt(Constants.SENSOR_PM25_ID).toFloat()
        var sensorVoc = plcClient.GetShortAt(Constants.SENSOR_VOC_ID).toFloat()
        var sensorCO2 = plcClient.GetShortAt(Constants.SENSOR_CO2_ID).toFloat()
        var systemStatus = SystemStatus(sensorTemp.toString(),sensorHumidity.toString(),sensorCO2,sensorVoc,sensorPM25,
            systemPower==Constants.POWER_ON,
            systemLijia==Constants.POWER_ON,
            "",systemMode,systemWind,systemJieNeng == Constants.POWER_ON,noFault)
        EventBus.getDefault().post(systemStatus)

        for (i in 0..roomSize-1){
            var temp = plcClient.GetShortAt(Constants.PANEL_TEMP_ID + i*Constants.PANEL_TEMP_LENGTH).toFloat()/10f
            var humidity = plcClient.GetShortAt(Constants.PANEL_HUMIDITY_ID + i*Constants.PANEL_TEMP_LENGTH).toFloat()/10f
            var setTemp = plcClient.GetShortAt(Constants.PANEL_SET_TEMP_ID + i*Constants.PANEL_TEMP_LENGTH)/10
            var power = plcClient.GetShortAt(Constants.PANEL_SWITCH_ID + i*Constants.PANEL_TEMP_LENGTH).toFloat()
            var panelStatus = PanelStatus(temp, setTemp,power == Constants.POWER_ON,humidity)
            panelHashMap.put(i,panelStatus)
        }
        EventBus.getDefault().post(panelHashMap)

        val deviceIdAttrArray = intArrayOf(3009, 3010, 3011, 3012, 3013, 3014)
        var deviceId = ""
        for (index in deviceIdAttrArray.indices) {
            var id = plcClient.GetByteAt(deviceIdAttrArray[index]).toInt()
            var subDeviceId = Integer.toHexString(id)
            if (subDeviceId.length == 1) {
                subDeviceId = "0$subDeviceId"
            }
            deviceId += subDeviceId
        }
        EventBus.getDefault().post(DeviceBean("$deviceId"))
    }

    fun plcUpdateSetting(){
        //VD1248
        var energy = plcClient.GetFloatAt(Constants.SETTING_ENERGY)
        var settingStatus = SettingStatus(energy)
        var holidayTimeStart = plcClient.GetFloatAt(Constants.SETTING_HOLIDAY_CLOCK_START).toInt()
        var holidayTimeEnd = plcClient.GetFloatAt(Constants.SETTING_HOLIDAY_CLOCK_END).toInt()
        var workTimeStart = plcClient.GetFloatAt(Constants.SETTING_WORK_CLOCK_START).toInt()
        var workTimeEnd = plcClient.GetFloatAt(Constants.SETTING_WORK_CLOCK_END).toInt()
        settingStatus.workDaySwitch = (plcClient.GetShortAt(Constants.SETTING_WORK_TIME_SWITCH) == Constants.POWER_ON.toInt())
        settingStatus.holidaySwitch = (plcClient.GetShortAt(Constants.SETTING_HOLIDAY_TIME_SWITCH) == Constants.POWER_ON.toInt())
        settingStatus.holidayClockTime = holidayTimeStart.toString() + "-" + holidayTimeEnd
        settingStatus.workDayClockTime = workTimeStart.toString() + "-" + workTimeEnd
        EventBus.getDefault().postSticky(settingStatus)
    }

    fun updatePlcFault(){
        //同步更新故障地址读取列表
        synchronized(plcFaultList) {
            //2.更新当前列表
            for (plcFault in plcFaultList) {
                var readValue: Float
                var shouldUpdate = false
                when (plcFault.addressUnit) {
                    Constants.VW -> {
                        readValue = plcClient.GetShortAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                    Constants.VB -> {
                        readValue = plcClient.GetByteAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                }
                if (shouldUpdate) {
                    //todo 故障有更新,通知界面
                    plcFault.toString().logi("PLC")
                    EventBus.getDefault().post(plcFault)
                }
            }
        }
    }

    /**
     * 更新
     */
    fun updatePlcSingleAddressList() {
        //同步更新plc读取地址列表
        synchronized(plcAttributeList) {
            //2.更新当前列表
            for (plcAttribute in plcAttributeList) {
                var shouldUpdate = false
                var readValue: Float
                when (plcAttribute.addressUnit) {
                    Constants.VW -> {
                        readValue = plcClient.GetShortAt(plcAttribute.offSet).toFloat()
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                    Constants.VD -> {
                        readValue = plcClient.GetFloatAt(plcAttribute.offSet)
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                    Constants.VB -> {
                        readValue = plcClient.GetByteAt(plcAttribute.offSet).toFloat()
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                }
                if (shouldUpdate) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
        //同步更新故障地址读取列表
        synchronized(plcFaultList) {
            //2.更新当前列表
            for (plcFault in plcFaultList) {
                var readValue: Float
                var shouldUpdate = false
                when (plcFault.addressUnit) {
                    Constants.VW -> {
                        readValue = plcClient.GetShortAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                    Constants.VB -> {
                        readValue = plcClient.GetByteAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                }
                if (shouldUpdate) {
                    //todo 故障有更新,通知界面
                    EventBus.getDefault().post(plcFault)
                }
            }
        }
    }

    /**
     * 通过Eventbus更新UI
     */
    private fun postPlcAttribute(plcAttribute: PlcAttribute) {
        "寄存器更新:$plcAttribute".logi()
        EventBus.getDefault().post(plcAttribute)
    }
}